/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ve.terminal.db;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.Logger;
import ve.terminal.controlador.Sesion;
import ve.terminal.excepciones.BaseDeDatosExcepcion;
import ve.terminal.excepciones.ConexionExcepcion;

/**
 *
 * @author Jesus Graterol
 */
public class MediadorBD {
    
    private static Connection conexion = null;
    private static final Logger logger = Logger.getLogger(MediadorBD.class);
    private static boolean conectado = false;
    
    /**
     * Crea la conexión con la base de datos del servidor
     * @throws ConexionExcepcion 
     */
    private static void conectar() throws ConexionExcepcion {
        if (logger.isDebugEnabled()) {
            logger.debug("conectar(boolean, boolean, String, Properties, boolean) - start");
        }

        try {
            Class.forName(Sesion.getDbClaseServidor());
        } catch (ClassNotFoundException ex) {
            logger.error("conectar(String)", ex);
            throw (new ConexionExcepcion("Falla de comunicación con la base de datos."
                    + "No se encuentra el Driver: " , ex));
        }
        try {
            conexion = DriverManager.getConnection(
                    Sesion.getDbUrlServidor(), 
                    Sesion.getDbUsuario(), 
                    Sesion.getDbClave());
            conectado = true;
        } catch (SQLException ex) {
            logger.error(
                        "conectar(String)",ex);
        throw (new ConexionExcepcion("Falla en la conexion a la BD Local. "
                + "Servidor inactivo, Url, Usuario o clave no valida. No se pudo conectar", ex));
        }
        if (logger.isDebugEnabled()) {
            logger.debug("conectar(String) - end");
        }
    }
    
    public static Connection getConexion() throws ConexionExcepcion{
        if (logger.isDebugEnabled()) {
                logger.debug("getConexion() - start");
        }

        boolean conexionActiva = false;
        Connection conexionUtilizada;
        conexionActiva = conectado;
        
        // Si la conexion no esta activa la creamos
        if (!conexionActiva)
            conectar();
        conexionUtilizada = conexion;
                

        if (logger.isDebugEnabled()) {
                logger.debug("getConexion() - end");
        }
        return conexion;	
    }
    
    /**
     * Realiza una sentencia de consulta contra la base de datos
     * @param sentenciaSql
     * @return ResultSet
     * @throws ConexionExcepcion
     * @throws BaseDeDatosExcepcion 
     */
    public static ResultSet realizarConsulta(String sentenciaSql) 
            throws ConexionExcepcion, BaseDeDatosExcepcion {
        if (logger.isDebugEnabled()) {
                logger.debug("realizarConsulta(String) - start");
        }

        ResultSet resultado = null;
        Connection conexionUtilizada = null;
        boolean conexionActiva;

        conexionActiva = conectado;
                

        // Si la conexion no esta activa la creamos
        if (!conexionActiva)
            conectar();
        conexionUtilizada = conexion;

        // Realizamos la operacion
        try {
            Statement sentencia = null;
            try {
                sentencia = conexionUtilizada.createStatement(
                        ResultSet.TYPE_SCROLL_INSENSITIVE, 
                        ResultSet.CONCUR_READ_ONLY);
                
            } catch (SQLException e) {
                logger.error("realizarConsulta(String)",	e);
                throw new ConexionExcepcion("Error al conectar a BD", e);
            }
            resultado = null;

            if (logger.isDebugEnabled()) {
                logger.debug("realizarConsulta(sentenciaSql = " + sentenciaSql
                                +") - Consulta SQL");
            }
            resultado = sentencia.executeQuery(sentenciaSql);
            resultado.first();
        } catch (SQLException exSQL){
            if (resultado != null) {
                try {
                    resultado.close();
                } catch (SQLException e) {
                    logger.error("realizarConsulta(String)",e);
                }
            }
            logger.error("realizarConsulta(String, boolean, boolean, boolean)",exSQL);
            conectar();
            throw (new BaseDeDatosExcepcion("Error al realizar sentencia", exSQL));
        }

        if (logger.isDebugEnabled()) {
                logger.debug("realizarConsulta(String) - end");
        }
        return resultado;
    }
    
    /**
     * Ejecuta una sentencia de actualización o eliminación contra la base de datos
     * @param sentenciaSql
     * @return Cantidad de filas afectadas por la sentencia
     * @throws ConexionExcepcion
     * @throws BaseDeDatosExcepcion 
     */
    public static int realizarSentencia(String sentenciaSql) 
            throws ConexionExcepcion, BaseDeDatosExcepcion {
        if (logger.isDebugEnabled()) {
            logger.debug("realizarSentencia(String, boolean, boolean) - start");
        }

        Connection conexionUtilizada;
        conexionUtilizada = getConexion();
        // Realizamos la operacion
        PreparedStatement sentencia = null;
        try {
            sentencia = conexionUtilizada.prepareStatement(sentenciaSql);
        } catch (SQLException exSQL) {
            logger.error("realizarSentencia(String)", exSQL);
            throw new ConexionExcepcion("Error de Conexion", exSQL);
        }
        try {
            if (logger.isDebugEnabled()) {
                logger.debug("realizarSentencia(sentenciaSql = " + sentenciaSql
                                    + ") - Sentencia ejecutada");
            }
            int returnint = sentencia.executeUpdate();
            if (logger.isDebugEnabled()) {
                logger.debug("realizarSentencia(String) - end");
            }
            return returnint;
        } catch (SQLException exSQL) {
            logger.error("realizarSentencia(String)", exSQL);
            throw (new BaseDeDatosExcepcion("Error al ejecutar sentencia", exSQL));
        } finally {
            try {
                sentencia.close();
            } catch (SQLException e) {
                logger.error("realizarSentencia(String, boolean, boolean)", e);
            }
            sentencia = null;
        }
    }
    
    /**
     * Ejecuta un grupo de sentencias, puede ser atomico o no
     * @param loteSentencias
     * @param atomicas
     * @return Arreglo con la cantidad de filas afectadas por cada sentencia
     * @throws ConexionExcepcion
     * @throws BaseDeDatosExcepcion 
     */
    static int[] ejecutarLoteSentencias(Statement loteSentencias, boolean atomicas) 
            throws ConexionExcepcion, BaseDeDatosExcepcion {
        if (logger.isDebugEnabled()) {
                logger.debug("ejecutarLoteSentencias(Statement, boolean) - start");
        }

        Connection conexionUtilizada = getConexion();
        int[] filasAfectadas = null;
        try {
            if (atomicas){
                conexionUtilizada.setAutoCommit(false);
                conexionUtilizada.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            }

            filasAfectadas = loteSentencias.executeBatch();
            if (atomicas){
                conexionUtilizada.commit();
                conexionUtilizada.setAutoCommit(true);
            }
        }catch (SQLException exSQL) {
            logger.error("ejecutarLoteSentencias(Statement, boolean)",exSQL);

            try {
                if (atomicas){
                    conexionUtilizada.rollback();
                    conexionUtilizada.setAutoCommit(true);
                }
            } catch (SQLException e) {
                    logger.error("ejecutarLoteSentencias(Statement, boolean)",e);
                    e.printStackTrace(System.out);
            }
            conectar();
            throw (new BaseDeDatosExcepcion("Error al realizar lote de sentencias " + loteSentencias, exSQL));
        }

        if (logger.isDebugEnabled()) {
            logger.debug("ejecutarLoteSentencias(Statement, boolean) - end");
        }
        return filasAfectadas;
    }
    
    /**
     * Crea un lote de sentencias a ser ejecutado
     * @return
     * @throws ConexionExcepcion 
     */
    public static Statement crearLoteSentencia () throws ConexionExcepcion {
        if (logger.isDebugEnabled()) {
                logger.debug("crearSentencia(boolean, boolean) - start");
        }

        Statement s = null;
        try {
                s = getConexion().createStatement();
        } catch (SQLException e) {
            logger.error("crearSentencia(boolean, boolean)", e);
            throw new ConexionExcepcion("Error de Conexion con BD", e);
        }

        if (logger.isDebugEnabled()) {
                logger.debug("crearSentencia(boolean, boolean) - end");
        }
        return s;
    }
    
    /**
     * Cierra la conexion con la base de datos
     */
    public static synchronized void cerrarConexion() {
        if (logger.isDebugEnabled()) {
            logger.debug("cerrarConexion() - start");
        }

        try {
            conexion.close();
        } catch (SQLException e) {
            logger.error("cerrarConexion()", e);
        } finally {
            conexion = null;
            conectado = false;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("cerrarConexion() - end");
        }
    }
}

